zhouqijie

OpenGL几何着色器

几何着色器位于光栅化之前的 图元处理 的管线段内。
几何着色器允许同时访问图元中的所有顶点。(即逐图元处理)

允许的操作:

  1. 输出原图元保持不变。
  2. 输出修改了顶点位置的图元。
  3. 输出不同类型的图元。
  4. 输出更多的其他图元。
  5. 删除图元(不输出)。

相关函数和内置变量:

EmitVertex();:完成了顶点属性相关计算并准备输出顶点。 EndPrimitive();:完成了组成图元的一组顶点的定义。

gl_PrimitiveIDIn:输入的图元ID。

修改图元

图元输出类型选项:

使用layout(...) in指定;

  1. points - 每次调用顶点数量:1
  2. lines - 每次调用顶点数量:2
  3. lines_adjacency - 每次调用顶点数量:4
  4. triangles - 每次调用顶点数量:3
  5. triangles_adjacency - 每次调用顶点数量:6

示例一:膨胀效果

//基本原理:把顶点沿着其法线移动。
void main (void)
{	
    for (int i=0; i<3; i++)
	{	gl_Position = proj_matrix *
			(gl_in[i].gl_Position + normalize(vec4(varyingNormal[i],1.0))*0.4);
		varyingNormalG = varyingNormal[i];
		varyingLightDirG = varyingLightDir[i];
		varyingHalfVectorG = varyingHalfVector[i];
		EmitVertex();//完成了顶点属性相关计算并准备输出顶点。
	}
	EndPrimitive();//完成了组成图元的一组顶点的定义。
}

示例一:爆炸效果

//基本原理:把顶点沿着图元上顶点的法线平均值(即三角形面法向量)移动。
void main (void)
{	vec4 triangleNormal = 
		vec4(((varyingNormal[0]+varyingNormal[1]+varyingNormal[2])/3.0),1.0);
	
	for (int i=0; i<3; i++)
	{	gl_Position = proj_matrix *
			(gl_in[i].gl_Position + normalize(triangleNormal)*0.4);
		varyingNormalG = varyingNormal[i];
		varyingLightDirG = varyingLightDir[i];
		varyingHalfVectorG = varyingHalfVector[i];
		EmitVertex();
	}
	EndPrimitive();
}

删除图元

不进行其他操作,直接调用EndPrimitive()来实现删除图元。

示例:删除特定图元

void main (void)
{	if (mod(gl_PrimitiveIDIn,3)!=0)
	{	for (int i=0; i<3; i++)
		{	gl_Position = proj_matrix * gl_in[i].gl_Position;
			varyingNormalG = varyingNormal[i];
			varyingLightDirG = varyingLightDir[i];
			varyingHalfVectorG = varyingHalfVector[i];
			EmitVertex();
		}
	}
	EndPrimitive();
}

添加图元

示例:在三角形图元中间插入点变成三个三角形图元:

vec3 newPoints[], lightDir[];
float sLen = 0.005;

void setOutputValues(int p, vec3 norm)
{	varyingNormal = norm;
	varyingLightDir = lightDir[p];
	varyingVertPos = newPoints[p];
	gl_Position = proj_matrix * vec4(newPoints[p], 1.0);
}

void makeNewTriangle(int p1, int p2)
{	// generate vertex normal for this triangle
	vec3 c1 = normalize(newPoints[p1] - newPoints[3]);
	vec3 c2 = normalize(newPoints[p2] - newPoints[3]);
	vec3 norm = cross(c1,c2);
	
	// generate and emit the three vertices
	setOutputValues(p1, norm); EmitVertex();
	setOutputValues(p2, norm); EmitVertex();
	setOutputValues(3, norm); EmitVertex();
	EndPrimitive();
}

void main(void)
{	// offset the three triangle vertices by the surface normal
	vec3 sp0 = gl_in[0].gl_Position.xyz + varyingOriginalNormal[0]*sLen;
	vec3 sp1 = gl_in[1].gl_Position.xyz + varyingOriginalNormal[1]*sLen;
	vec3 sp2 = gl_in[2].gl_Position.xyz + varyingOriginalNormal[2]*sLen;
	
	// compute the new points comprising a small pyramid
	newPoints[0] = gl_in[0].gl_Position.xyz;
	newPoints[1] = gl_in[1].gl_Position.xyz;
	newPoints[2] = gl_in[2].gl_Position.xyz;
	newPoints[3] = (sp0 + sp1 + sp2)/3.0;	// spike point
	
	// compute the directions from the vertices to the light
	lightDir[0] = light.position - newPoints[0];
	lightDir[1] = light.position - newPoints[1];
	lightDir[2] = light.position - newPoints[2];
	lightDir[3] = light.position - newPoints[3];

	// build three new triangles to form a small surface pyramid
	makeNewTriangle(0,1);  // the third point is always the spike point
	makeNewTriangle(1,2);
	makeNewTriangle(2,0);
}

修改图元类型

示例:三角形图元修改成放射状的线图元:

void main(void)
{	// offset the three triangle vertices by the surface normal
	vec3 op0 = gl_in[0].gl_Position.xyz;
	vec3 op1 = gl_in[1].gl_Position.xyz;
	vec3 op2 = gl_in[2].gl_Position.xyz;
	vec3 ep0 = gl_in[0].gl_Position.xyz + varyingNormal[0]*sLen;
	vec3 ep1 = gl_in[1].gl_Position.xyz + varyingNormal[1]*sLen;
	vec3 ep2 = gl_in[2].gl_Position.xyz + varyingNormal[2]*sLen;
	
	// compute the new points comprising a small line segment
	vec3 newPoint1 = (op0 + op1 + op2)/3.0;	// start point
	vec3 newPoint2 = (ep0 + ep1 + ep2)/3.0;	// end point
	
	gl_Position = proj_matrix * vec4(newPoint1, 1.0);
	varyingVertPosG = newPoint1;
	varyingLightDirG = light.position - newPoint1;
	varyingNormalG = varyingNormal[0];
	EmitVertex();
	
	gl_Position = proj_matrix * vec4(newPoint2, 1.0);
	varyingVertPosG = newPoint2;
	varyingLightDirG = light.position - newPoint2;
	varyingNormalG = varyingNormal[1];
	EmitVertex();
	
	EndPrimitive();
}